﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Threading;
using System.Threading;
using System.Diagnostics;

using JavaSubstitutes;
using Sonar;

namespace Sprites
{
	//this class basically runs the game and has nothing specific to do with the Mario sprite per se
	public class MarioComponent : FrameworkElement
	{
		private const long serialVersionUID = 739318775993206607L;
		//public const int FRAMES_PER_SECOND = 24;
		//public const int FRAMES_PER_SECOND = 36;
		public const int FRAMES_PER_SECOND = 60;

		public bool running = false; //todo: made public for testing
		private int width, height;
		private GraphicsConfiguration graphicsConfiguration;
		//private Scene mapScene;
		private SonarSoundEngine sound;
		//private bool focused = false;
		private bool useScale2x = false;
		public Scene currentScene; //todo: made public for testing
		public MapScene mapScene;//todo:made public for testing
        //private LevelScene levelScene;

        //int lastTick = -1;
        int renderedFrames = 0;
        int fps = 0;
        
		protected DispatcherTimer dispatchTimer;

		private Scale2x scale2x = new Scale2x(320, 240);

		public MarioComponent(): base()
		{
		}

		public MarioComponent(int width, int height): this()
		{
			this.width = width;
			this.height = height;

			try
			{
				sound = new SonarSoundEngine(64);
			}
			catch (Exception e)
			{
				sound = new FakeSoundEngine();
			}
		}

		public void start(Canvas layoutRoot)
		{
			if (!running)
			{
				running = true;

				dispatchTimer = new DispatcherTimer() { Interval = new TimeSpan(0, 0, 0, 0, 1000 / FRAMES_PER_SECOND) }; //~24fps
				dispatchTimer.Tick += (sender, args) =>
					{
						run(layoutRoot);
					};
				dispatchTimer.Start();
			}
		}

		public void stop()
		{
			Art.stopMusic();
			running = false;
		}

        //todo: public for testing- should be prot' virt
		public void run(Canvas layoutRoot)
		{
			if (graphicsConfiguration == null) graphicsConfiguration = new GraphicsConfiguration(layoutRoot);

            Art.init(graphicsConfiguration, sound);

            WriteableBitmap bmpScreen = new WriteableBitmap(320, 240);

			if (currentScene == null) StartGame(bmpScreen);

			//ShowTitleScreen(); //TODO: enable this functionality eventually
			currentScene.tick();						
			currentScene.render(bmpScreen, 1);

            //ShowStartGameScene();

            if(!(currentScene is MapScene))
                ToggleClickToPlay(bmpScreen);
			
            //Scale2x();//TODO: implement, should be easy via scale transform

            renderedFrames++;

            bmpScreen.Invalidate();
			layoutRoot.Children.RemoveAllButType(typeof(MediaElement)); //don't nuke the sound player
            layoutRoot.Children.Add(new Image() { Source = bmpScreen });
		}

		private void StartGame(WriteableBitmap bmpScreen)
		{
			mapScene = new MapScene(graphicsConfiguration, this, RandomExtensions.nextLong());
			currentScene = mapScene;
			currentScene.setSound(sound);
			ShowTitleScreen();
			currentScene.render(bmpScreen, 1);

			ShowStartGameScene();
			currentScene.render(bmpScreen, 1);
		}
        
        private void ToggleClickToPlay(WriteableBitmap bmp)
        {
            //if (!this.hasFocus()) && tick / 4 % 2 == 0) //origina
            //TODO: mapScene has :
            //		private void drawStringDropShadow(WriteableBitmap wbmp, String text, int x, int y, int c)
            //and
            //private void drawString(WriteableBitmap wbmp, String text, int x, int y, int c)
            //should prob be in Art, and remove this bit below
            if (!hasFocus() && renderedFrames % 20 >= 2) //meliot: looks lke it's trying to flash on or off, if the dispatcherTimer is 50ms, then %20 is every 1/5th second basically
            {
                String msg = "CLICK TO PLAY";

                drawString(bmp, msg, 160 - msg.Length() * 4 + 1, 110 + 1, 0); //meliot: this appears to be done twice to get a drop shadow effect
                drawString(bmp, msg, 160 - msg.Length() * 4, 110, 7); //consider: generic dropshow method
            }
        }

        private static void Scale2x()
        {
            return;
            //if (width != 320 || height != 240)
            //{
            //    if (useScale2x)
            //    {
            //        wbmp.drawImage(scale2x.scale(wbmp), 0, 0, null);
            //    }
            //    else
            //    {
            //        wbmp.drawImage(wbmp, 0, 0, 640, 480, null);
            //    }
            //}
            //else
            //{
            //    wbmp.drawImage(wbmp, 0, 0, null);
            //}

            //wbmp.drawImage(wbmp, 0, 0, 640, 480, null);
        }

        private bool hasFocus()
		{
            //meliot: I think the actual point of this is whether or not the game has started
            //return FocusManager.GetFocusedElement() == layoutRoot;
            return true;
		}

        //todo: this ought to be moved to a utility method in Art
		private void drawString(WriteableBitmap wbmp, String text, int x, int y, int c)
		{
			char[] ch = text.toCharArray();
			for (int i = 0; i < ch.Length; i++)
			{
				wbmp.drawImage(Art.font[ch[i] - 32, c], x + i * 8, y);
			}
		}

		public void startLevel(int seed, int difficulty, int type)
		{
			currentScene = new LevelScene(graphicsConfiguration, this, seed, difficulty, type);
			currentScene.setSound(sound);
			currentScene.init();
		}                                               

		public void ShowLevelFailedScene()
		{
			currentScene = mapScene;
            mapScene.startMusic();
			Mario.lives--;
			if (Mario.lives == 0)
			{
				ShowLoseScene();
			}
		}

		public void ShowLevelWonScene()
		{
			currentScene = mapScene;
            mapScene.startMusic();
            mapScene.levelWon();
		}

		public void ShowWinScene()
		{
			currentScene = new WinScene(this);
			currentScene.setSound(sound);
			currentScene.init();
		}

        public void ShowTitleScreen()
		{
			Mario.Reset();
			currentScene = new TitleScene(this, graphicsConfiguration);
			currentScene.setSound(sound);
			currentScene.init();
		}

		public void ShowLoseScene()
		{
			currentScene = new LoseScene(this);
			currentScene.setSound(sound);
			currentScene.init();
		}

		public void ShowStartGameScene()
		{
			currentScene = mapScene;
            mapScene.startMusic();
            mapScene.init();
		}
        
        //returns whether it handled the key or not
        public virtual bool toggleKey(Key keyCode, bool isPressed)
        {
            switch (keyCode)
            {
                case Key.Left:
                    currentScene.toggleKey(Mario.KEY_LEFT, isPressed);
                    return true;

                case Key.Right:
                    currentScene.toggleKey(Mario.KEY_RIGHT, isPressed);
                    return true;

                case Key.Down:
                    currentScene.toggleKey(Mario.KEY_DOWN, isPressed);
                    return true;

                case Key.Up:
                    currentScene.toggleKey(Mario.KEY_UP, isPressed);
                    return true;

                case Key.A:
                    currentScene.toggleKey(Mario.KEY_SPEED, isPressed);
                    return true;

                case Key.S:
                case Key.Space:
                    currentScene.toggleKey(Mario.KEY_JUMP, isPressed);
                    return true;

                case Key.F1:
                    useScale2x = !useScale2x;
                    return true;

                default: return false;
            }
        }

	}
}